<?php

namespace Tools\Root\HttpClient;

/**
 * Class Authority
 * @package Tools\Root\HttpClient;
 */
class Authority
{
    /**
     * Handle version.
     *
     * @var float
     */
    private float $version = 0;

    /**
     * Permissions persist the storage file.
     *
     * @var string
     */
    private string $loadDataFile = '';

    /**
     * Data.
     *
     * @var array
     */
    private array $parameterList = [];

    /**
     * Authority constructor.
     *
     * @param string $loadDataFile
     * @throws \Exception
     */
    public function __construct(string $loadDataFile)
    {
        $this->loadDataFile = $loadDataFile;
    }

    /**
     * Setter.
     *
     * @param string $key
     * @param int|string $value
     * @return bool
     */
    public function set(string $key, $value)
    {
        $this->parameterList[$key] = $value;

        return true;
    }

    /**
     * Getter.
     *
     * @param string $key
     * @param null|mixed $default
     * @return mixed|null
     */
    public function get(string $key, $default = null)
    {
        return $this->parameterList[$key] ?? $default;
    }

    /**
     * Append a key-value, if key exists will return false.
     *
     * @param string $key
     * @param $value
     * @return bool
     */
    public function append(string $key, $value)
    {
        if (array_key_exists($key, $this->parameterList)) return false;

        return $this->set($key, $value);
    }

    /**
     * Delete a key.
     *
     * @param string $key
     * @return bool
     */
    public function delete(string $key)
    {
        unset($this->parameterList[$key]);

        return true;
    }

    /**
     * Initialize.
     *
     * @throws \Exception
     */
    public function initialize()
    {
        /**
         * Parse key.
         *
         * @param string $key
         * @return string
         */
        $keyPare = function (string $key) {
            return trim($key, ' ');
        };

        /**
         * Parse value.
         *
         * @param string $value
         * @return string
         */
        $valuePare = function (string $value) {
            return trim($value, ' ');
        };

        /**
         * Parse line.
         *
         * @param string $line
         * @return array [key, value]
         */
        $lineParse = function (string $line) use ($keyPare, $valuePare) {
            $result = explode(':', $line);

            return [$keyPare($result[0]), $valuePare($result[1])];
        };



        if (!is_file($this->loadDataFile)) throw new \Exception('Not a file!');

        $handle = fopen($this->loadDataFile, 'rb');

        if ($handle === false) throw new \Exception('Open the file failure!');

        if (flock($handle, LOCK_SH | LOCK_NB)) {
            while (($line = fgets($handle)) !== false) {
                [$key, $value] = $lineParse(trim($line));

                if ($key === 'version') {
                    $this->version = (float) $value;
                } else {
                    $this->set($key, $value);
                }
            }

            flock($handle, LOCK_UN);
        }

        fclose($handle);
    }

    /**
     * Persistent.
     *
     * @param bool $isReplace
     * @return bool
     * @throws \Exception
     */
    public function persistent(bool $isReplace = false)
    {
        /**
         * Parse key.
         *
         * @param string $key
         * @return string
         */
        $keyPare = function (string $key) {
            return trim($key, ' ');
        };

        /**
         * Parse value.
         *
         * @param string $value
         * @return string
         */
        $valuePare = function (string $value) {
            return trim($value, ' ');
        };

        /**
         * Parse line.
         *
         * @param string $line
         * @return array [key, value]
         */
        $lineParse = function (string $line) use ($keyPare, $valuePare) {
            $result = explode(':', $line);

            return [$keyPare($result[0]), $valuePare($result[1])];
        };

        if (!is_file($this->loadDataFile)) throw new \Exception('Not a file!');

        $handle = fopen($this->loadDataFile, 'rb+');

        if (flock($handle, LOCK_EX | LOCK_NB)) {
            if (!$isReplace) {
                // find version, match version value
                while (($line = fgets($handle)) !== false) {
                    [$key, $value] = $lineParse($line);

                    if ($key === 'version' && $this->version == $value) {
                        flock($handle, LOCK_UN);
                        fclose($handle);

                        return false;
                    }
                }
            }

            // data replace
            $version = $this->version + 1; // version change
            ftruncate($handle, 0);
            rewind($handle);

            fwrite($handle, "version: {$version}" . PHP_EOL);
            foreach ($this->parameterList as $key => $value) {
                fwrite($handle, "{$key}: {$value}" . PHP_EOL);
            }

            flock($handle, LOCK_UN);
        }

        fclose($handle);

        $this->version = $version;

        return true;
    }

    /**
     * @return string
     */
    public function getLoadDataFile(): string
    {
        return $this->loadDataFile;
    }

    /**
     * @param string $loadDataFile
     */
    public function setLoadDataFile(string $loadDataFile): void
    {
        $this->loadDataFile = $loadDataFile;
    }

    /**
     * @return array
     */
    public function getParameterList(): array
    {
        return $this->parameterList;
    }

    /**
     * @param array $parameterList
     */
    public function setParameterList(array $parameterList): void
    {
        $this->parameterList = $parameterList;
    }
}
